-- DPSCheck-Hunter.lua

local current_class, current_class_filename = UnitClass('player')
if current_class_filename ~= 'HUNTER' then
  return
end

------------------------------------------------------------
-- shared utility functions --------------------------------
------------------------------------------------------------

local get = DPSCheck_get
local get2 = DPSCheck_get2
local update_sum = DPSCheck_update_sum

------------------------------------------------------------
-- spell ids -----------------------------------------------
------------------------------------------------------------

local auto_shot_spell_id, auto_shot_spell_name = DPSCheck.populate_spell(75)
local aimed_shot_spell_id, aimed_shot_spell_name = DPSCheck.populate_spell(19434)
local aimed_shot2_spell_id, aimed_shot2_spell_name = DPSCheck.populate_spell(82928)
local arcane_shot_spell_id = DPSCheck.populate_spell(3044)
local black_arrow_spell_id = DPSCheck.populate_spell(3674)
local chimera_shot_spell_id, chimera_shot_spell_name = DPSCheck.populate_spell(53209)
local cobra_shot_spell_id, cobra_shot_spell_name = DPSCheck.populate_spell(77767)
local explosive_shot_spell_id, explosive_shot_spell_name = DPSCheck.populate_spell(53301)
local fire_spell_id, fire_spell_name = DPSCheck.populate_spell(82926)
local focus_fire_spell_id, focus_fire_spell_name = DPSCheck.populate_spell(82692)
local frenzy_effect_spell_id, frenzy_effect_spell_name = DPSCheck.populate_spell(19615)
local hunters_mark_spell_id = DPSCheck.populate_spell(1130)
local intimidation_spell_id = DPSCheck.populate_spell(19577)
local improved_steady_shot_spell_id, improved_steady_shot_spell_name = DPSCheck.populate_spell(53220)
local kill_command_spell_id, kill_command_spell_name = DPSCheck.populate_spell(34026)
local kill_shot_spell_id, kill_shot_spell_name = DPSCheck.populate_spell(53351)
local lock_and_load_spell_id, lock_and_load_spell_name = DPSCheck.populate_spell(56453)
local marked_for_death_spell_id = DPSCheck.populate_spell(88691)
local rapid_fire_spell_id, rapid_fire_spell_name = DPSCheck.populate_spell(23989)
local readiness_spell_id = DPSCheck.populate_spell(23989)
local serpent_sting_spell_id, serpent_sting_spell_name = DPSCheck.populate_spell(1978)
local steady_shot_spell_id, steady_shot_spell_name = DPSCheck.populate_spell(56641)

------------------------------------------------------------
-- event matching ------------------------------------------
------------------------------------------------------------

local push_event_pattern = DPSCheck.push_event_pattern

push_event_pattern(function (current_state, combat_segment, input_event)
                     -- don't worry about double counting clipping and priority mistakes.

                     local spell_stats = combat_segment.statistics.priorities[input_event.spell_id]
                     if spell_stats == nil then
                       return
                     end

                     update_sum(spell_stats.mistakes, 'WAIT', 1)
                   end,
                   {event = 'COMBAT_LOG_EVENT_UNFILTERED', subevent = 'SPELL_AURA_REFRESH', source_flags = 1297, spell_id = explosive_shot_spell_id})

-- auto shot tracking

push_event_pattern(function (current_state, combat_segment, input_event)
                     update_sum(combat_segment.statistics, 'auto_shot_missed', 1)
                     update_sum(combat_segment.statistics, 'auto_shot_lost', 1)
                   end,
                   {event = 'COMBAT_LOG_EVENT_UNFILTERED', subevent = 'RANGE_MISSED', source_flags = 1297, miss_type = 'MISS'})

push_event_pattern(function (current_state, combat_segment, input_event)
                     update_sum(combat_segment.statistics, 'auto_shot_melee', 1)
                     update_sum(combat_segment.statistics, 'auto_shot_lost', 1)
                   end,
                   {event = 'COMBAT_LOG_EVENT_UNFILTERED', subevent = 'SWING_DAMAGE', source_flags = 1297})

push_event_pattern(function (current_state, combat_segment, input_event)
                     update_sum(combat_segment.statistics, 'auto_shot_melee_missed', 1)
                     update_sum(combat_segment.statistics, 'auto_shot_lost', 1)
                   end,
                   {event = 'COMBAT_LOG_EVENT_UNFILTERED', subevent = 'SWING_MISSED', source_flags = 1297, miss_type = 'MISS'})

push_event_pattern(function (current_state, combat_segment, input_event)
                     update_sum(combat_segment.statistics, 'auto_shot_failed', 1)
                     
		     local ranged_attack_speed = UnitRangedDamage('player')
                     if ranged_attack_speed ~= nil and ranged_attack_speed > 0 then
                       update_sum(combat_segment.statistics, 'auto_shot_lost', 0.5 / ranged_attack_speed)
                     end
                   end,
                   {event = 'UNIT_SPELLCAST_FAILED_QUIET', spell_name = auto_shot_spell_name, unit_id = 'player'})

push_event_pattern(function (current_state, combat_segment, input_event)
                     update_sum(combat_segment.statistics, 'auto_shot_succeeded', 1)
                     DPSCheck.check_auras(auto_shot_spell_name)
                   end,
                   {event = 'UNIT_SPELLCAST_SUCCEEDED', spell_name = auto_shot_spell_name, unit_id = 'player'})

push_event_pattern(function (current_state, combat_segment, input_event)
                     current_state.talents = input_event.talents
                   end,
                   {}, 
                   {'talents'})

------------------------------------------------------------
-- reporting functions -------------------------------------
------------------------------------------------------------

local class_checks_category = current_class .. ' Checks'

local push_priority_function = DPSCheck.push_priority_function

local function push_report_function(report_name, report_description, report_func)
  DPSCheck.push_report_function(report_name, class_checks_category, report_description, report_func)
end

push_report_function('auto shots', 
                     'Reports how many Auto Shots were fired versus an estimated maximum possible number of Auto Shots.',
                     function (combat_segment)
   		       -- missed are included in lost and succeeded already, so adjust below
                       local missed = get(combat_segment.statistics, 'auto_shot_missed', 0)

                       local succeeded = get(combat_segment.statistics, 'auto_shot_succeeded', 0) - missed
                       local lost = get(combat_segment.statistics, 'auto_shot_lost', 0)
                       local melee = get(combat_segment.statistics, 'auto_shot_melee', 0) + get(combat_segment.statistics, 'auto_shot_melee_missed', 0)
                       local failed = get(combat_segment.statistics, 'auto_shot_failed', 0)

                       local total = succeeded + lost
                       if total <= 0 then
                         return
                       end

                       if missed > 0 then
                         missed_comment = format('%d missed auto shots', missed)
                       end

                       if melee > 0 then
                         melee_comment = format('%d melee swings', melee)
                       end

                       if failed > 0 then
                         failed_comment = format('%d failed auto shots', failed)
                       end

                       comment = ''
                       if missed > 0 then
                         comment = missed_comment
                       end

                       if melee > 0 then
                         if comment == '' then
                           comment = melee_comment
                         else
                           comment = comment .. ', ' .. melee_comment
                         end
                       end

                       if failed > 0 then
                         if comment == '' then
                           comment = failed_comment
                         else
                           comment = comment .. ', ' .. failed_comment
                         end
                       end

                       return {succeeded, total, comment}
                     end)

push_priority_function(readiness_spell_id,
                       'Readiness resets all cooldowns, so Rapid Fire should always be used beforehand. (Rapid Fire is off the GCD.)',
               	       function (current_spell_name, available_spell_names)
                         if available_spell_names[rapid_fire_spell_name] ~= nil then
                           return rapid_fire_spell_name
                         end

                         return
                       end)

local function pick_cobra_steady()
  if IsSpellKnown(cobra_shot_spell_id) then
    return cobra_shot_spell_name
  else
    -- this will only be called for characters with talent
    -- specialization chosen, so they better have steady shot.
    return steady_shot_spell_name
  end
end

local function prioritize_shots_bm(available_spell_names)
  local save_focus = false
  if IsSpellKnown(kill_command_spell_id) then
    if available_spell_names[kill_command_spell_name] then
      return kill_command_spell_name
    end

    local _, kill_command_cooldown = GetSpellCooldown(kill_command_spell_id)
    if kill_command_cooldown == 0 then
      -- already ran out of focus
      save_focus = true
    else
      -- TODO: implement focus regen calculation here
    end
  end

  if IsSpellKnown(focus_fire_spell_id) then
    local _, _, _, frenzy_count = UnitAura('pet', frenzy_effect_spell_name, nil, 'HELPFUL')
    if (frenzy_count ~= nil) and (frenzy_count >= 5) then
      return focus_fire_spell_name
    end
  end

  if save_focus then
    return pick_cobra_steady()
  end

  -- TODO: implement real focus dump/saving
  return nil
end

local function prioritize_shots_mm(available_spell_names)
  -- chimera shot has priority over everything else and drives focus saving
  local save_focus = false
  if IsSpellKnown(chimera_shot_spell_id) then
    if available_spell_names[chimera_shot_spell_name] then
      return chimera_shot_spell_name
    end

    local _, chimera_shot_cooldown = GetSpellCooldown(chimera_shot_spell_id)
    if chimera_shot_cooldown == 0 then
      save_focus = true
    else
      -- TODO: implement focus regen calculation here
    end
  end

  -- free aimed shot is dps burst, and only skipped to avoid wasting focus. 
  -- aimed shot is always available with full focus cost and casting time, so
  -- don't need to check availability
  local fire_temp = UnitAura('player', fire_spell_name, nil, 'HELPFUL')
  if fire_temp ~= nil then
    return aimed_shot_spell_name
  end

  if save_focus then
    -- TODO : should this move before aimed shot check?
    return steady_shot_spell_name
  end

  -- temporary before focus saving implemented
  return nil
end

local function prioritize_shots_sv(available_spell_names)
  local explosive_shot_dot = false
  local lock_and_load_proc = false

  if available_spell_names[explosive_shot_spell_name] then
    local es_temp = UnitAura('target', explosive_shot_spell_name, nil, 'HARMFUL|PLAYER')
    if es_temp == nil then
      -- only return explosive shot if the debuff has fallen off
      return explosive_shot_spell_name
    end
    explosive_shot_dot = true

    local lnl_temp = UnitAura('player', lock_and_load_spell_name, nil, 'HELPFUL')
    if lnl_temp ~= nil then
      lock_and_load_proc = true
    end
  end

  local _, explosive_shot_cooldown = GetSpellCooldown(explosive_shot_spell_id)
  if explosive_shot_cooldown == 0 then
    if not explosive_shot_dot then
      -- if explosive shot is off cooldown, and there is no dot, then
      -- we want to fire but ran out of focus.
      return pick_cobra_steady()
    end
  end

  local save_focus = false
  -- TODO: implement focus regen calculation here
  -- TODO: focus dump for lock and load

  return nil

  --if (not lock_and_load_proc) and (not save_focus) then
  --  return arcane_shot_spell_name
  --end

  -- assuming we always prefer Cobra Shot to Steady Shot
  --return pick_cobra_steady()
end

local function prioritize_shots(current_spell_name, available_spell_names)
  local priority_spell_name = nil

  if available_spell_names[kill_shot_spell_name] then
    -- Kill Shot has priority in all specs
    priority_spell_name = kill_shot_spell_name
  elseif IsSpellKnown(intimidation_spell_id) then
    priority_spell_name = prioritize_shots_bm(available_spell_names)
  elseif IsSpellKnown(aimed_shot_spell_id) then
    priority_spell_name = prioritize_shots_mm(available_spell_names)
  elseif IsSpellKnown(explosive_shot_spell_id) then
    priority_spell_name = nil -- prioritize_shots_sv(available_spell_names)
  end

  if priority_spell_name ~= current_spell_name then
    return priority_spell_name
  else
    return nil
  end
end

push_priority_function(kill_command_spell_id,
                       "Kill Command is Beast Mastery's signature shot and has priority over all shots but Kill Shot.",
                       prioritize_shots)

push_priority_function(focus_fire_spell_id, 
                       'Focus Fire provides a stacking haste buff and has priority after Kill Shot and Kill Command.',
                       prioritize_shots)

push_priority_function(chimera_shot_spell_id,
                       "Chimera Shot is Marksmanship's signature shot and has priority over all shots but Kill Shot.",
                       prioritize_shots)

push_priority_function(aimed_shot_spell_id,
                       'Aimed Shot is used when the Fire! buff makes it instant. Aimed Shot has priority after Kill Shot and Chimera Shot.',
                       prioritize_shots)

--push_priority_function(explosive_shot_spell_id,
--                       "Explosive Shot is Survival's signature shot and has priority over all shots but Kill Shot. However, Explosive Shot may be delayed to avoid clipping its own DoT.",
--               	       prioritize_shots)

push_priority_function(arcane_shot_spell_id,
                       'Arcane Shot is a focus dump shot with low priority.',
                       prioritize_shots)

push_priority_function(cobra_shot_spell_id,
                       'Cobra Shot is a focus generating shot which extends the duration of Serpent Sting.',
                       prioritize_shots)

push_priority_function(steady_shot_spell_id,
		       'Steady Shot is a focus generating shot. For Marksmanship builds, it also provides a haste buff.',
                       prioritize_shots)

------------------------------------------------------------
-- buff/debuff checks --------------------------------------
------------------------------------------------------------

-- Hunter's Mark for basically everything

-- Improved Steady Shot for Auto Shot, Steady / Cobra Shot

-- Serpent Sting for Chimera or all SV

DPSCheck.add_debuff_check(hunters_mark_spell_id,
                          "Hunter's Mark and Marked for Death buff ranged attack power for all shots.",
                          {hunters_mark_spell_id, marked_for_death_spell_id},
                          {aimed_shot_spell_id, aimed_shot2_spell_id, arcane_shot_spell_id, auto_shot_spell_id, chimera_shot_spell_id, cobra_shot_spell_id, serpent_sting_spell_id, steady_shot_spell_id},
			  {spell_id = hunters_mark_spell_id})

DPSCheck.add_debuff_check(serpent_sting_spell_id,
                          'Serpent Sting is refreshed by Chimera Shot and Cobra Shot. (Noxious Stings support in a later release.)',
                          {serpent_sting_spell_id},
                          {chimera_shot_spell_id, cobra_shot_spell_id},
                          {spell_id = serpent_sting_spell_id, selfonly = true})

DPSCheck.add_buff_check(improved_steady_shot_spell_id,
                        'Improved Steady Shot gives a nice ranged attack speed buff.',
                        {improved_steady_shot_spell_id},
                        {auto_shot_spell_id, steady_shot_spell_id, cobra_shot_spell_id},
                        {talent = improved_steady_shot_spell_name})

DPSCheck.add_buff_check(fire_spell_id,
                        'Aimed Shot should only be fired when the Fire! is up.',
                        {fire_spell_id},
                        {aimed_shot_spell_id, aimed_shot2_spell_id},
                        {talent = 'Master Marksman'}) -- TODO : international support
